package nl.zoidberg.calculon.analyzer;

import java.io.BufferedReader;
import java.io.FileWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import nl.zoidberg.calculon.engine.CheckDetector;
import nl.zoidberg.calculon.engine.MoveGenerator;
import nl.zoidberg.calculon.model.Board;
import nl.zoidberg.calculon.model.Piece;

public class GameScorer {
	private static GameScorer instance = getUnweightedScorer();
	
//	public static void main(String[] args) throws Exception {
//		loadScorer();
//		System.out.println(new File(".").getAbsolutePath());
//	}
	
	private static GameScorer getUnweightedScorer() {
		GameScorer rv = new GameScorer();
		rv.addScorer(new MaterialScorer(), 1.0f);
		rv.addScorer(new BishopPairScorer(), 1.0f);
		rv.addScorer(new BishopMobilityScorer(), 1.0f);
		rv.addScorer(new PawnStructureScorer(), 1.0f);
		rv.addScorer(new KnightScorer(), 1.0f);
		rv.addScorer(new RookScorer(), 1.0f);
		rv.addScorer(new KingSafetyScorer(), 1.0f);
		return rv;
	}
	
	private Map<PositionScorer, Float> scorers = new HashMap<PositionScorer, Float>();
	
	public static GameScorer getDefaultScorer() {
		return instance;
	}
	
	public static GameScorer loadScorer() throws Exception {
		GameScorer rv;
		InputStream is = ClassLoader.getSystemResourceAsStream("scorers.sav");
		if(is == null) {
			rv = getUnweightedScorer();
			Map<PositionScorer, Float> newScorers = new HashMap<PositionScorer, Float>();
			for(PositionScorer scorer: rv.scorers.keySet()) {
				newScorers.put(scorer, new Float(0.75f + (float) (Math.random()/2.0f)));
			}
			rv.scorers = newScorers;
			saveScorer(rv);
		} else {
			rv = new GameScorer();
			BufferedReader br = new BufferedReader(new InputStreamReader(is));
			String s;
			while((s = br.readLine()) != null) {
				StringTokenizer st = new StringTokenizer(s, "=");
				PositionScorer scorer = (PositionScorer) Class.forName(st.nextToken()).newInstance();
				rv.addScorer(scorer, Float.parseFloat(st.nextToken()));
			}
			br.close();
		}
		return rv;
	}
	
	public static void saveScorer(GameScorer scorer) throws Exception {
		PrintWriter pw = new PrintWriter(new FileWriter("scorers.sav"));
		for(PositionScorer sc: scorer.scorers.keySet()) {
			pw.println(sc.getClass().getName() + "=" + scorer.scorers.get(sc));
		}
		pw.close();
	}

	public void addScorer(PositionScorer scorer, float weighting) {
		scorers.put(scorer, weighting);
	}
	
	/**
	 * Generate a score - positive is good for the current player. Position scorers however can stick with the 
	 * convention of having white as positive.
	 * 
	 * @param board
	 * @return
	 */
	public float score(Board board) {
		if(board.getHalfMoveCount() >= 100) {
			return 0; // Draw by 50 move rule
		}
		
		if( ! MoveGenerator.get().isMovePossible(board)) {
			if(CheckDetector.alreadyInCheck(board)) {
				//return board.getPlayer() == Piece.WHITE ? -100 : 100;  // Checkmate
				return -100;
			} else {
				return 0;  // Stalemate
			}
		}
		
		if(board.getRepeatedCount() >= 3) {
			return 0;
		}
		
		float score = 0;
		Map<Byte, List<int[]>> pieceMap = generatePieceMap(board);
		
		for(PositionScorer scorer: scorers.keySet()) {
			score += (scorer.scorePosition(board, pieceMap) * scorers.get(scorer));
		}

		return score * (board.getPlayer() == Piece.WHITE ? 1 : -1);
	}
	
	public static Map<Byte, List<int[]>> generatePieceMap(Board board) {
		Map<Byte, List<int[]>> pieceMap = new HashMap<Byte, List<int[]>>();
		for(int file = 0; file < 8; file++) {
			for(int rank = 0; rank < 8; rank++) {
				byte rPiece = board.getPiece(file, rank);
				if(rPiece == 0) {
					continue;
				}
				List<int[]> locations = pieceMap.get(rPiece);
				if(locations == null) {
					locations = new ArrayList<int[]>();
					pieceMap.put(rPiece, locations);
				}
				locations.add(new int[] { file, rank });
			}
		}
		return pieceMap;
	}
}
